package iterators;

import config.GlobalConfig;
import controlFlowGraph.*;
import domains.AnalysisDomain;

public abstract class FixpointIterator {

	public abstract void doIteration(CFG cfg);
	
	public static int narrowingSteps = 0;

	protected AnalysisDomain recompute(CFGNode node) {
		// end / start node in backward / forward analysis -> keep initial values
		if (node.getInEdges().isEmpty() && GlobalConfig.isForward()) {
			return node.getAnalysisInfo();
		}
		if (node.getOutEdges().isEmpty() && GlobalConfig.isBackward()) {
			return node.getAnalysisInfo();
		}
		
		// Non empty input / output edges -> bottom cannot reach combinators below
		// -> should work properly now
		// COMPUTING F_I
		AnalysisDomain res = GlobalConfig.analysis.getBottom();
		AnalysisDomain newVal = GlobalConfig.analysis.getBottom();
		if (GlobalConfig.isForward()) {
			for (CFGEdge edge : node.getInEdges()) {
				CFGLabel lab = edge.getLabel();
				if (lab instanceof LabAssign) {
					newVal = GlobalConfig.analysis.edgeEffectAssign(edge);
				} else if (lab instanceof LabLoad) {
					newVal = GlobalConfig.analysis.edgeEffectLoad(edge);
				} else if (lab instanceof LabStore) {
					newVal = GlobalConfig.analysis.edgeEffectStore(edge);
				} else if (lab instanceof LabNeg) {
					newVal = GlobalConfig.analysis.edgeEffectNeg(edge);
				} else if (lab instanceof LabPos) {
					newVal = GlobalConfig.analysis.edgeEffectPos(edge);
				} else if (lab instanceof LabNop) {
					newVal = GlobalConfig.analysis.edgeEffectNOP(edge);
				} else {
					System.out.println("Strange edge!");
				}
				res = GlobalConfig.analysis.join(res, newVal);
			}
		} else {
			for (CFGEdge edge : node.getOutEdges()) {
				CFGLabel lab = edge.getLabel();
				if (lab instanceof LabAssign) {
					newVal = GlobalConfig.analysis.edgeEffectAssign(edge);
				} else if (lab instanceof LabLoad) {
					newVal = GlobalConfig.analysis.edgeEffectLoad(edge);
				} else if (lab instanceof LabStore) {
					newVal = GlobalConfig.analysis.edgeEffectStore(edge);
				} else if (lab instanceof LabNeg) {
					newVal = GlobalConfig.analysis.edgeEffectNeg(edge);
				} else if (lab instanceof LabPos) {
					newVal = GlobalConfig.analysis.edgeEffectPos(edge);
				} else if (lab instanceof LabNop) {
					newVal = GlobalConfig.analysis.edgeEffectNOP(edge);
				} else {
					System.out.println("Strange edge!");
				}
				res = GlobalConfig.analysis.join(res, newVal);
			}
		}
		
		// Combination, enforces monotonicity
		if (GlobalConfig.iterPhase == IterationPhase.NORMAL) {
			res = GlobalConfig.analysis.join(res, node.getAnalysisInfo());
		} else if (GlobalConfig.iterPhase == IterationPhase.WIDENING) {
			res = GlobalConfig.analysis.widening(node.getAnalysisInfo(), res);
		} else if (GlobalConfig.iterPhase == IterationPhase.ACCNARROWING ||
				(GlobalConfig.iterPhase == IterationPhase.NARROWING && narrowingSteps > GlobalConfig.maxNarrowingSteps)) {
			res = GlobalConfig.analysis.narrowing(node.getAnalysisInfo(), res);
		} else if (GlobalConfig.iterPhase == IterationPhase.NARROWING) {
			res = GlobalConfig.analysis.meet(node.getAnalysisInfo(), res);
			narrowingSteps++;
		} else {
			System.out.println("Iteration Phase not implemented! Exiting ...");
			System.exit(-1);
		}
		return res;
	}

}
